# -*- awk -*-
# EMACS awk mode works quite well for nginx configs

# eotk (c) 2019 Alec Muffett

# SECURITY NOTE: the contents of this file, when actualised, should
# not be made world-readable nor published without redaction;
# password-like 128-bit "nonces" are used in the static regexps which
# substitute hostnames.  It a leak occurs: simply rebuild the
# configurations (which will create new nonces) and redeploy.

# logs and pids
pid %PROJECT_DIR%/nginx.pid;
error_log %LOG_DIR%/nginx-error.log %NGINX_SYSLOG%;
%%IF %NGINX_MODULES_DIRS%
%%CSV %NGINX_MODULES_DIRS%
include %1%/*.conf;
%%ENDCSV
%%ELSE
# no nginx_modules_dirs specified
%%ENDIF

# TODO: notes for custom 403 error-handling pages:
# https://www.cyberciti.biz/faq/unix-linux-nginx-custom-error-403-page-configuration/
# https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page

# performance
%%IF %IS_SOFTMAP%
worker_processes %SOFTMAP_NGINX_WORKERS%; # softmap
%%ELSE
worker_processes %NGINX_WORKERS%; # hardmap
%%ENDIF
worker_rlimit_nofile %NGINX_RLIM%;
events {
  worker_connections %NGINX_RLIM%;
}

http {
  # nginx fails without large enough buckets (sigh)
  map_hash_bucket_size %NGINX_HASH_BUCKET_SIZE%;
  server_names_hash_bucket_size %NGINX_HASH_BUCKET_SIZE%;

  # dns for proxy (sigh)
  resolver %NGINX_RESOLVER% valid=%NGINX_TIMEOUT%s;
  resolver_timeout %NGINX_TIMEOUT%s;

  # we walk a line between keeping it small and flooding resources...
  proxy_buffering on;

  # for initial; impacts SSL header
  proxy_buffer_size %NGINX_BLOCK_SIZE%;

  # for rest of response
  proxy_buffers %NGINX_BLOCK_COUNT% %NGINX_BLOCK_SIZE%;

  # how much can be busy sending to client?
  proxy_busy_buffers_size %NGINX_BLOCK_BUSY_SIZE%;

  # where to stash oversize requests?
  client_body_temp_path /tmp/nginx-body-%PROJECT%;
  client_max_body_size 4m;

  # in case we want to start spooling responses locally
  proxy_temp_path /tmp/nginx-proxy-%PROJECT%;
  proxy_max_temp_file_size %NGINX_TMPFILE_SIZE%;
  proxy_temp_file_write_size %NGINX_BLOCK_SIZE%;

  %%IF %NGINX_CACHE_SECONDS%
  # nginx caching static responses for %NGINX_CACHE_SECONDS% seconds
  # - this is a lightweight cache to reduce "storms", hence the global
  # approch of "cache everything for a small number of seconds"
  # https://nginx.org/en/docs/http/ngx_http_proxy_module.html
  proxy_cache_path /tmp/nginx-cache-%PROJECT% levels=1:2 keys_zone=%PROJECT%:%NGINX_CACHE_SIZE%;
  proxy_cache %PROJECT%;
  proxy_cache_min_uses %NGINX_CACHE_MIN_USES%;
  proxy_cache_revalidate on;
  proxy_cache_use_stale timeout updating;
  proxy_cache_valid any %NGINX_CACHE_SECONDS%s; # "any" includes 404s, etc

  # content-types to not cache
  map $http_content_type $no_cache_content_type {
    %%CSV %NO_CACHE_CONTENT_TYPE%
    %1% 1;
    %%ENDCSV
    default 0;
  }

  # hosts not to cache
  map $http_host $no_cache_host {
    hostnames;
    %%CSV %NO_CACHE_HOST%
    %1% 1;
    %%ENDCSV
    default 0;
  }

  # so, should we skip caching this stuff for some reason?
  proxy_no_cache $no_cache_content_type $no_cache_host;
  proxy_cache_bypass $no_cache_content_type $no_cache_host;
  %%ELSE
  # nginx caching disabled
  %%ENDIF

  # logs
  access_log %LOG_DIR%/nginx-access.log;

  # global settings
  server_tokens off;

  # allow/deny (first wins)
  allow "unix:";
  deny all;

  # rewrite these content types; text/html is implicit
  subs_filter_types
  application/javascript
  application/json
  application/x-javascript
  text/css
  text/javascript
  text/xml
  %%IF %EXTRA_SUBS_FILTER_TYPES%
  # extra_subs_filter_types
  %EXTRA_SUBS_FILTER_TYPES%
  %%ELSE
  # no extra_subs_filter_types
  %%ENDIF
  ;

  # Frankly, I am horrified at the use of "$1" to drive the
  # subs_filter replacement string for the preamble capture, in case
  # the onion address is "2bcdef..." and the resulting replacement
  # string is "$12bcdef..." - which you would expect to be capture
  # group "$12"; however it seems that subs_filter source code
  # hard-limits capture groups to 9, and attempts to use "${1}" and
  # "\\g{1}" so far have all failed, therefore we stick with it...

  %%IF %PRESERVE_CSV%
  # preserve subs (save-phase): 1=description,2=re,3=i_or_empty,4=replacement
  %%CSV %PRESERVE_CSV%
  # saving regexp '%2%' as '%1%' for replacement with '%4%' (%3%)
  subs_filter
  "(%PRESERVE_PREAMBLE_RE%)(%2%)\\b"
  "$1%PRESERVE_BEFORE%%1%%PRESERVE_AFTER%"
  g%3%r
  ;
  %%ENDCSV
  %%ELSE
  # no preserve subs (save-phase)
  %%ENDIF

  %%BEGIN
  # map: %DNS_DOMAIN% -> %ONION_ADDRESS%
  subs_filter
  "(%LEFT_TLD_RE%)%DNS_DOMAIN_RE2%\\b"
  "$1%ONION_ADDRESS%"
  gir
  ;
  # map: %DNS_DOMAIN_RE% -> %ONION_ADDRESS_RE%
  subs_filter
  "(%LEFT_TLD_RE%)%DNS_DOMAIN_RE4%\\b"
  "$1%ONION_ADDRESS_RE2%"
  gir
  ;
  %%IF %HARD_MODE% > 1
  # hard2 map: %DNS_DOMAIN_RE2% -> %ONION_ADDRESS_RE2%
  subs_filter
  "(%LEFT_TLD_RE%)%DNS_DOMAIN_RE8%\\b"
  "$1%ONION_ADDRESS_RE4%"
  gir
  ;
  # hard2 map: %DNS_DOMAIN_RE3% -> %ONION_ADDRESS_RE3%
  subs_filter
  "(%LEFT_TLD_RE%)%DNS_DOMAIN_RE12%\\b"
  "$1%ONION_ADDRESS_RE6%"
  gir
  ;
  %%ENDIF

  %%END

  %%IF %FOREIGNMAP_CSV%
  # foreignmap subs: 1=onion,2=re,3=re2,4=dns,5=re,6=re2
  %%CSV %FOREIGNMAP_CSV%
  # for %4% -> %1%
  subs_filter
  "(%LEFT_TLD_RE%)%6%\\b"
  "$1%1%"
  gir
  ;
  %%ENDCSV
  %%ELSE
  # no foreignmap subs
  %%ENDIF

  %%IF %PRESERVE_CSV%
  # preserve subs (restore-phase): 1=description,2=re,3=i_or_empty,4=replacement
  %%CSV %PRESERVE_CSV%
  # restoring '%1%' with '%4%'
  subs_filter
  "%PRESERVE_BEFORE%%1%%PRESERVE_AFTER%"
  "%4%"
  g
  ;
  %%ENDCSV
  %%ELSE
  # no preserve subs (restore-phase)
  %%ENDIF

  # o_to_d_lookup -> if cannot remap, return input.  note: old versions
  # of lua-plugin cannot cope with code like o_to_d_mappings[o[1]]
  # because of `long bracket syntax`; the `[o[` freaks it out.
  # See: https://github.com/openresty/lua-nginx-module/issues/748
  init_by_lua_block {
    -- helper functions for elsewhere

    -- builds data structures
    -- http://www.lua.org/pil/11.5.html
    TrueMap = function (list)
      local set = {}
      for _, l in ipairs(list) do set[l] = true end
      return set
    end

    -- for compression sanity-testing
    is_compression = TrueMap{ "br", "compress", "deflate", "gzip", }

    -- for neutering entire uris to be filled with "<!--EOTK-->"
    is_neutered_uri = TrueMap{
        -- "/path/goes/here",
        -- ...
    }

    -- for debug messages
    Slog = function (s) -- in case of manual debugging
      ngx.log(ngx.ERR, s)
      return
    end

    -- for matching uri suffixes, etc
    HasSuffix = function (s, x)
      return string.sub(s, -string.len(x)) == x
    end

    -- mapping onions to dns

    o_to_d_mappings = {}
    %%BEGIN
    o_to_d_mappings["%ONION_ADDRESS%"] = "%DNS_DOMAIN%"
    %%END

    -- 1st element is the TLD boundary prefix, probably an empty string, maybe '2f'
    -- 2nd element is the onion
    o_to_d_lookup = function (m)
      local prefix = m[1]
      local k = m[2]
      local v = ( o_to_d_mappings[k] or k )
      if (prefix == "") then -- fast happy-path response
        return v
      end
      return prefix .. v
    end

    onion_to_dns = function (i)
      if i == nil or i == "" then
        return i
      end
      if (type(i) == "table") then
        local j, k, result
        result = {}
        for j, k in ipairs(i) do
          table.insert(result, onion_to_dns(k))
        end
        return result
      end
      local o, num, errs = ngx.re.gsub(i, "(%LEFT_TLD_RE%)([a-z2-7]{16}(?:[a-z2-7]{40})?\\.onion)\\b", o_to_d_lookup, "io")
      if errs == nil and num == 0 then
        return i -- nothing was changed, so return the original
      end
      return o
    end

    -- mapping dns to onions, for experimentation

    d_to_o_mappings = {}
    %%BEGIN
    d_to_o_mappings["%DNS_DOMAIN%"] = "%ONION_ADDRESS%"
    %%END

    d_to_o_lookup = function (m)
      local k = m[1] -- see note above re: array syntax
      return ( d_to_o_mappings[k] or k )
    end

    dns_to_onion = function (i)
      if i == nil or i == "" or i == "*" then
        return i
      end
      if (type(i) == "table") then
        local j, k, result
        result = {}
        for j, k in ipairs(i) do
          table.insert(result, dns_to_onion(k))
        end
        return result
      end
      local num, errs
      %%BEGIN
      i, num, errs = ngx.re.gsub(i, "(%LEFT_TLD_RE%)%DNS_DOMAIN_RE2%\\b", "${1}%ONION_ADDRESS%", "io")
      %%END
      return i
    end
  }

  # filter the response headers en-route back to the user
  header_filter_by_lua_block {
    -- did upstream miss our request for 'identity'?
    local ce = ngx.var.upstream_http_content_encoding or ""
    if is_compression[ce] then
      Slog("compressed data returned from origin: "..ce)
      %%IF %DROP_UNREWRITABLE_CONTENT%
      -- I'd prefer to do something nice like this:
      --   ngx.status = 520
      --   ngx.say("upstream content was compressed and therefore not rewritable")
      --   ngx.exit(ngx.OK)
      -- ...but say() needs an API that is not available in this phase:
      --   https://github.com/openresty/lua-nginx-module#header_filter_by_lua
      -- therefore:
      ngx.exit(520) -- en.wikipedia.org/wiki/List_of_HTTP_status_codes
      %%ELSE
      -- UNREWRITABLE CONTENT NOT DROPPED, COMPRESSION ONION LEAKS POSSIBLE
      %%ENDIF
    end

    local k, v

    local origin_rewrites = {
      "Access-Control-Allow-Origin",
      %%IF %SUPPRESS_HEADER_CSP%
      -- CSP headers are suppressed via SUPPRESS_HEADER_CSP
      %%ELSE
      "Content-Security-Policy",
      "Content-Security-Policy-Report-Only",
      %%ENDIF
      "Link",
      "Location",
      "Set-Cookie",
      "Timing-Allow-Origin"
    }

    local i, k
    for i, k in ipairs(origin_rewrites) do
      local v = ngx.header[k]
      if v then
        ngx.header[k] = dns_to_onion(v)
      end
    end

    %%IF %DEBUG_CSP_SANDBOX%
    local csp_sandbox =
    "sandbox allow-forms allow-modals allow-orientation-lock"..
    " allow-pointer-lock allow-popups allow-popups-to-escape-sandbox"..
    " allow-presentation allow-same-origin allow-scripts;"
    ngx.header["Content-Security-Policy"] = csp_sandbox
    %%ELSE
    -- no debug csp sandboxing
    %%ENDIF
  }

  %%IF %DEBUG_TRAP%
  # debug trap: filter the response body en-route back to the user
  # hello! you have found the debug_trap code, which is not
  # well-documented but adds repeated chunks of very slow code to
  # capture/log any server-supplied content matching the supplied
  # regular expressions, with up-to-64 chars of preceding and trailing
  # context; use with caution, and probably do not put this into
  # production, against ALL CONTENT INCLUDING (eg:) VIDEO; this code
  # exists to help debug situations where some HTML or JS content is
  # being passed through NGINX to the browser, but you don't know how
  # it got there.
  # config usage: set debug_trap foo\\.regex\\.tld [...]
  body_filter_by_lua_block {
    -- change auxh to Content-Encoding or Set-Cookie or whatever, if needed
    local auxh = "Access-Control-Allow-Origin"

    local i = ngx.arg[1]
    local ct = ngx.header["Content-Type"] or ""
    local aux = ngx.header[auxh] or ""
    local uri = ngx.var.uri or ""
    local iterator, err
    %%CSV %DEBUG_TRAP%
    iterator, err = ngx.re.gmatch(i, ".{0,64}(%1%).{0,64}", "io")
    if not iterator then
      ngx.log(ngx.ERR, "gmatch error: ", err)
    else
      while true do
        local m, err = iterator()
        if err then
          ngx.log(ngx.ERR, "iterator error: ", err)
          break
        end
        if not m then
          break
        end
        local msg =
          string.format("\n\tTRAP %s\n"..
                        "\tTYPE %s\n"..
                        "\tAUXH %s: %s\n"..
                        "\tCODE %s\n"..
                        "\tURI %s\n",
                        m[1],
                        ct,
                        auxh, aux,
                        m[0],
                        uri)
        Slog(msg)
      end -- while true
    end -- if iterator
    %%ENDCSV

    -- search and replace; this would be faster to do with a linear CSV unwind,
    -- as you could benefit from the regexp optimiser in-place + tune casei
    local edit_map = {} -- example config follows:
    -- edit_map["regular\\.expression"] = "replacement.string"
    -- edit_map["insertion.preamble"] = "${0}inserted.code"
    -- ...

    for regexp, replacement in pairs(edit_map) do
      local m, err = ngx.re.match(ngx.arg[1], regexp, "i")
      if m then -- could just gsub it blindly but i want the logs
        Slog("REPLACE: "..m[0].." BY "..replacement.."\n")
        local i, num, errs = ngx.re.gsub(ngx.arg[1], regexp, replacement, "i")
        ngx.arg[1] = i
      end
    end

    -- neutering, if any (return "safe" content)
    if is_neutered_uri[uri] then
      Slog("neutering: "..uri)
      ngx.arg[1] = "<!--EOTK-->"
    end
  }
  %%ELSE
  # no debug traps
  %%ENDIF

  %%IF %SUPPRESS_HEADER_CSP%
  # csp suppression
  proxy_hide_header "Content-Security-Policy";
  proxy_hide_header "Content-Security-Policy-Report-Only";
  %%ELSE
  # csp not suppressed, will be rewritten instead, see below
  %%ENDIF

  %%IF %SUPPRESS_HEADER_HSTS%
  # hsts suppression
  proxy_hide_header "Strict-Transport-Security";
  %%ELSE
  # hsts not suppressed
  %%ENDIF

  %%IF %SUPPRESS_HEADER_HPKP%
  # hpkp suppression
  proxy_hide_header "Public-Key-Pins";
  proxy_hide_header "Public-Key-Pins-Report-Only";
  %%ELSE
  # hpkp not suppressed
  %%ENDIF

  # global proxy settings
  proxy_read_timeout %NGINX_TIMEOUT%;
  proxy_connect_timeout %NGINX_TIMEOUT%;

  # SSL config
  ssl_certificate %SSL_DIR%/%CERT_PREFIX%.cert;
  ssl_certificate_key %SSL_DIR%/%CERT_PREFIX%.pem;
  ssl_buffer_size 4k;
  #ssl_ciphers 'EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES256'; ## LibreSSL, OpenSSL 1.1.0+
  ssl_ciphers 'EECDH+AESGCM:EECDH+AES256'; ## OpenSSL 1.0.1% to 1.0.2%
  ssl_ecdh_curve prime256v1;
  #ssl_ecdh_curve secp384r1:prime256v1; ## NGINX nginx 1.11.0 and later
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;

  # websockets: on the basis of http_upgrade, set connection_upgrade:
  # empty -> empty
  # default -> "upgrade"
  map $http_upgrade $connection_upgrade {
    default "upgrade";
    "" "";
  }

  %%BEGIN
  %%IF %FORCE_HTTPS%
  # FORCE_HTTPS is in use; set up separate server for port 80 & force redirects
  server {
    %%IF %IS_SOFTMAP%
    %%RANGE I 1 %SOFTMAP_TOR_WORKERS%
    listen unix:%PROJECT_DIR%/%TOR_WORKER_PREFIX%-%I%.d/port-80.sock;
    %%ENDRANGE
    %%ELSE
    listen unix:%PROJECT_DIR%/%ONION_DIRNAME%/port-80.sock;
    %%ENDIF

    # subdomain regexp captures trailing dot, use carefully; does not need "~*"
    # NB: this regexp should be kept in-sync with the other FORCE_HTTPS copy
    server_name
    %ONION_ADDRESS%
    ~^(?<servernamesubdomain>([-0-9a-z]+\\.)+)%ONION_ADDRESS_RE2%$
    ;

    %%IF %SUPPRESS_TOR2WEB%
    # suppress tor2web traffic; "let them use clearnet"
    if ( $http_x_tor2web ) {
      return 403 "%BLOCK_ERR%";
    }
    %%ELSE
    # tor2web not suppressed
    %%ENDIF

    # tell the client to try again as HTTPS without ever leaving the onion
    # use 307 / temporary redirect because your URIs may change in future
    # use $host (not $server) to copy-over subdomains, etc, transparently
    # SEND BACK ORIGINAL PARAMS, FIX THEM ONLY UPON FORWARD TO THE PROXY.
    return 307 https://$host$request_uri;
  }
  %%ELSE
  # FORCE_HTTPS is not in use, cleartext data may traverse the internet
  %%ENDIF

  # for %ONION_ADDRESS% -> %DNS_DOMAIN%
  server {
    %%IF %IS_SOFTMAP%
    %%RANGE I 1 %SOFTMAP_TOR_WORKERS%
    %%IF not %FORCE_HTTPS%
    # FORCE_HTTPS is not in use, cleartext data may traverse the internet
    listen unix:%PROJECT_DIR%/%TOR_WORKER_PREFIX%-%I%.d/port-80.sock;
    %%ENDIF
    listen unix:%PROJECT_DIR%/%TOR_WORKER_PREFIX%-%I%.d/port-443.sock ssl;
    %%ENDRANGE
    %%ELSE
    # hardmap
    # unix sockets; use <ONION_ADDRESS>.d as a naming convention
    %%IF not %FORCE_HTTPS%
    # FORCE_HTTPS is not in use, cleartext data may traverse the internet
    listen unix:%PROJECT_DIR%/%ONION_DIRNAME%/port-80.sock;
    %%ENDIF
    listen unix:%PROJECT_DIR%/%ONION_DIRNAME%/port-443.sock ssl;
    %%ENDIF

    # subdomain regexp captures trailing dot, use carefully; does not need "~*"
    # NB: this regexp should be kept in-sync with the other FORCE_HTTPS copy
    server_name
    %ONION_ADDRESS%
    ~^(?<servernamesubdomain>([-0-9a-z]+\\.)+)%ONION_ADDRESS_RE2%$
    ;

    %%INCLUDE templates.d/nginx-generated-blocks.conf

    %%IF %COOKIE_LOCK%
    # if we are visiting the magic path, open the cookie-lock
    location "%COOKIE_LOCK%" {
      add_header Set-Cookie "eotk_lock=%COOKIE_LOCK%;Domain=.%ONION_ADDRESS%;Path=/;Max-Age=604800";
      return 200 "OK";
    }
    %%ELSE
    # no cookie_lock cookie setting
    %%ENDIF

    %%IF %NGINX_HELLO_ONION%
    # for test & to help SSL certificate acceptance
    location ~ "^/hello[-_]onion/?$" {
      return 200 "Hello, Onion User!";
    }
    %%ELSE
    # no "hello-onion" endpoint
    %%ENDIF

    %%IF %HARDCODED_ENDPOINT_CSV%
    # hardcoded_endpoints: 1=path_re,2=response
    %%CSV %HARDCODED_ENDPOINT_CSV%
    location ~ "%1%" {
      return 200 %2%;
    }
    %%ENDCSV
    %%ELSE
    # no hardcoded_endpoints
    %%ENDIF

    %%IF exists templates.d/nginx-site-%ONION_ADDRESS%.conf
    # A note on spliced content; any file that you splice into the
    # final configuration MUST NOT contain any EOTK-variables, nor any
    # EOTK template directives, simply because these will all be
    # ignored and may cause NGINX syntax errors.  The `splice` hack is
    # meant for small bits of production-ready NGINX config that MUST
    # occur only in the definition of one particular onion out of
    # (presumably several) in the eventual configuration file; most
    # likely this will be something like: per-onion access control.

    # The SPLICE directive is executed at template-generation time,
    # and is subject to flow-control like IF/ELSE/ENDIF; by contrast
    # the INCLUDE directive is executed at template-load time, and is
    # invisible to the IF/ELSE/ENDIF flow control; in this sense the
    # INCLUDE directive is more like "#include" from C/C++

    # ---- BEGIN SPLICE: templates.d/nginx-site-%ONION_ADDRESS%.conf ----
    %%SPLICE templates.d/nginx-site-%ONION_ADDRESS%.conf
    # ---- END SPLICE: templates.d/nginx-site-%ONION_ADDRESS%.conf ----
    %%ELSE
    # splice: no file: templates.d/nginx-site-%ONION_ADDRESS%.conf
    %%ENDIF

    # for traffic
    location / {
      %%INCLUDE templates.d/nginx-generated-checks.conf

      %%IF %COOKIE_LOCK%
      # check for cookie-lock
      if ( $cookie_eotk_lock != "%COOKIE_LOCK%" ) { %NGINX_ACTION_ABORT%; }
      %%ELSE
      # no cookie-lock checks
      %%ENDIF

      # deonionify the request_uri for forwarding (both path and args)
      set_by_lua_block $request_uri2 {
        return onion_to_dns(ngx.var.request_uri)
      }

      %%IF %DEONIONIFY_POST_BODIES%
      # deonionify_post_bodies (specifically the body; path and args come as standard)
      access_by_lua_block {
        if ngx.req.get_method() == "POST" then
          ngx.req.read_body()
          local old = ngx.req.get_body_data()
          local new = onion_to_dns(old)
          if new ~= old then
            ngx.req.set_body_data(new)
          end
        end
      }
      %%ELSE
      # no deonionify_post_bodies
      %%ENDIF

      # note use of both $scheme and the deonionified uri (both path and args)
      set $new_url "$scheme://${servernamesubdomain}%DNS_DOMAIN%$request_uri2";
      proxy_pass $new_url;
      proxy_http_version 1.1;

      # a note on proxy_set_header, add_header, similar methods, etc;
      # if you override *any* header then you will lose the other
      # headers inherited from the parent contexts:
      # https://blog.g3rt.nl/nginx-add_header-pitfall.html

      proxy_set_header X-From-Onion %X_FROM_ONION_VALUE%;
      proxy_set_header Host "${servernamesubdomain}%DNS_DOMAIN%";
      proxy_set_header Accept-Encoding "identity";
      proxy_set_header Connection $connection_upgrade; # SSL
      proxy_set_header Upgrade $http_upgrade; # SSL
      proxy_ssl_server_name on; # SSL

      # rewrite request referer
      set_by_lua_block $referer2 { return onion_to_dns(ngx.var.http_referer) }
      proxy_set_header Referer $referer2;

      # rewrite request origin
      set_by_lua_block $origin2 { return onion_to_dns(ngx.var.http_origin) }
      proxy_set_header Origin $origin2;

      # rewrite request cookies
      set_by_lua_block $cookie2 { return onion_to_dns(ngx.var.http_cookie) }
      proxy_set_header Cookie $cookie2;

      %%IF %SUPPRESS_METHODS_EXCEPT_GET%
      # suppress non-GET methods (e.g.: POST)
      limit_except GET {
        deny all;
      }
      %%ELSE
      # non-GET methods (e.g.: POST) are not suppressed
      %%ENDIF
    }
  }

  %%END

  %%IF %DEBUG_ORIGIN_HEADERS%
  more_set_headers "EOTK-Upstream: ct=$upstream_http_content_type;ce=$upstream_http_content_encoding"
  %%ELSE
  # origin headers not debugged
  %%ENDIF

  # header purge
  more_clear_headers "Age";
  more_clear_headers "Server";
  more_clear_headers "Via";
  more_clear_headers "X-From-Nginx";
  more_clear_headers "X-NA";
  more_clear_headers "X-Powered-By";
  more_clear_headers "X-Request-Id";
  more_clear_headers "X-Runtime";
  more_clear_headers "X-Varnish";
}
